package com.temp.websocket;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Optional;
import java.util.concurrent.*;

@Component
@ServerEndpoint("/admin/chat")
@Slf4j
public class AdminWebSocketEndpoint {

    private static final String ADMIN_KEY = "admin";
    private static final ConcurrentHashMap<String, SessionInfo> adminSessions = new ConcurrentHashMap<>();

    // 使用方案2的可靠调度器创建方式
    private static final ScheduledExecutorService scheduler = createHeartbeatScheduler();

    private static final long HEARTBEAT_INTERVAL = 30;
    private static final long HEARTBEAT_TIMEOUT = 60;
    private static final String PING_MESSAGE = "PING";
    private static final String MESSAGE_FORMAT = "from:%s,content:%s";
    private static final String MESSAGE_REGEX = ",content:";
    private static final String TO_PREFIX = "to:";

    /**
     * 创建可靠的心跳调度器
     */
    private static ScheduledExecutorService createHeartbeatScheduler() {
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(
                2, // 核心线程数
                new ThreadFactory() {
                    private int threadCount = 0;

                    @Override
                    public Thread newThread(Runnable r) {
                        Thread thread = new Thread(r, "admin-ws-heartbeat-" + (threadCount++));
                        thread.setDaemon(true);
                        thread.setPriority(Thread.NORM_PRIORITY);
                        return thread;
                    }
                },
                new ThreadPoolExecutor.CallerRunsPolicy()
        );

        // 配置调度器参数
        executor.setRemoveOnCancelPolicy(true);
        executor.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
        executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);

        log.info("✅ 客服心跳调度器创建成功: 核心线程数={}", executor.getCorePoolSize());
        return executor;
    }

    // 静态初始化块测试调度器
    static {
        log.info("AdminWebSocketEndpoint类加载完成");

        // 立即测试调度器是否工作
        scheduler.schedule(() -> {
            log.info("✅ 客服调度器初始化测试：心跳调度器正常工作");
        }, 1, TimeUnit.SECONDS);

        // 添加关闭钩子
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            log.info("正在关闭客服心跳调度器...");
            scheduler.shutdown();
            try {
                if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                    scheduler.shutdownNow();
                }
            } catch (InterruptedException e) {
                scheduler.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }));
    }

    /**
     * 会话信息封装类
     */
    private static class SessionInfo {
        Session session;
        ScheduledFuture<?> heartbeatFuture;
        volatile long lastActiveTime;

        SessionInfo(Session session, ScheduledFuture<?> heartbeatFuture) {
            this.session = session;
            this.heartbeatFuture = heartbeatFuture;
            this.lastActiveTime = System.currentTimeMillis();
        }

        void updateActiveTime() {
            this.lastActiveTime = System.currentTimeMillis();
        }

        void cancelHeartbeat() {
            if (heartbeatFuture != null && !heartbeatFuture.isCancelled()) {
                heartbeatFuture.cancel(true);
                log.debug("客服心跳任务已取消");
            }
        }
    }

    @OnOpen
    public void onOpen(Session session) {
        log.info("客服开始连接, sessionId: {}", session.getId());

        // 检查是否已有客服在线
        if (isAdminOnline()) {
            closeSessionWithPolicy(session, "只允许一个客服在线");
            return;
        }

        // 设置会话属性
        configureSession(session);

        // 创建心跳检测任务
        ScheduledFuture<?> heartbeatFuture = scheduler.scheduleAtFixedRate(() -> {
            heartbeatTask();
        }, 5, HEARTBEAT_INTERVAL, TimeUnit.SECONDS); // 首次延迟5秒

        // 保存会话信息
        SessionInfo sessionInfo = new SessionInfo(session, heartbeatFuture);
        adminSessions.put(ADMIN_KEY, sessionInfo);

        log.info("客服已连接, sessionId: {}, 心跳任务已启动", session.getId());
    }

    /**
     * 独立的心跳任务方法
     */
    private void heartbeatTask() {
        try {
            log.info("❤️ 客服心跳任务执行");

            SessionInfo info = adminSessions.get(ADMIN_KEY);
            if (info == null) {
                log.warn("客服会话信息为null，停止心跳");
                return;
            }
            if (!info.session.isOpen()) {
                log.warn("客服会话已关闭，停止心跳");
                return;
            }

            long inactiveTime = System.currentTimeMillis() - info.lastActiveTime;
            log.debug("客服空闲时间: {}ms, 超时阈值: {}ms", inactiveTime, HEARTBEAT_TIMEOUT * 1000);

            // 检查超时
            if (inactiveTime > HEARTBEAT_TIMEOUT * 1000) {
                log.warn("客服心跳超时，断开连接");
                throw new IOException("心跳超时：" + inactiveTime + "ms");
            }

            // 发送Ping
            info.session.getBasicRemote().sendPing(ByteBuffer.wrap(PING_MESSAGE.getBytes()));
            log.info("✅ 向客服发送心跳PING");

        } catch (Exception e) {
            log.error("❌ 客服心跳检测失败: {}", e.getMessage());
            cleanupAdminSession();
        }
    }

    @OnClose
    public void onClose(Session session) {
        log.info("客服连接关闭, sessionId: {}", session.getId());
        SessionInfo info = adminSessions.get(ADMIN_KEY);
        if (info != null && info.session.getId().equals(session.getId())) {
            info.cancelHeartbeat();
            cleanupAdminSession();
            log.info("客服已断开, sessionId: {}", session.getId());
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("客服连接发生错误, sessionId: {}: {}", session.getId(), error.getMessage(), error);
        SessionInfo info = adminSessions.get(ADMIN_KEY);
        if (info != null && info.session.getId().equals(session.getId())) {
            info.cancelHeartbeat();
            cleanupAdminSession();
        }
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        log.debug("收到客服消息: {}, sessionId: {}", message, session.getId());

        // 更新最后活跃时间
        SessionInfo info = adminSessions.get(ADMIN_KEY);
        if (info != null) {
            info.updateActiveTime();
            if ("PING".equals(message)) {
                log.debug("收到客服的心跳PING请求");
                try {
                    session.getBasicRemote().sendText("PONG");
                    log.debug("向客服发送PONG响应");
                } catch (IOException e) {
                    log.error("发送PONG响应失败", e);
                }
            } else {
                parseMessage(message).ifPresent(parsed -> {
                    String toUserId = parsed[0];
                    String content = parsed[1];
                    sendToUser(Long.parseLong(toUserId), content);
                });
            }
        }
    }

    public static void sendToAdmin(Long fromUserId, String message) {
        getAdminSession().ifPresent(session -> {
            String formattedMessage = String.format(MESSAGE_FORMAT, fromUserId, message);
            sendTextMessage(session, formattedMessage);
        });
    }

    public static boolean isAdminOnline() {
        return getAdminSession()
                .map(Session::isOpen)
                .orElse(false);
    }

    private static void configureSession(Session session) {
        try {
            session.setMaxIdleTimeout(HEARTBEAT_INTERVAL * 1000 * 2);
        } catch (Exception e) {
            log.warn("设置会话属性失败: {}", e.getMessage());
        }
    }

    private static void closeSessionWithPolicy(Session session, String reason) {
        try {
            session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, reason));
            log.warn("拒绝重复客服连接, 原因: {}", reason);
        } catch (IOException e) {
            log.error("关闭会话失败: {}", e.getMessage());
        }
    }

    private static void cleanupAdminSession() {
        SessionInfo info = adminSessions.remove(ADMIN_KEY);
        if (info != null) {
            info.cancelHeartbeat();
            try {
                if (info.session.isOpen()) {
                    info.session.close();
                }
            } catch (IOException e) {
                log.error("关闭客服会话失败: {}", e.getMessage());
            }
            log.info("客服会话清理完成");
        }
    }

    private static Optional<String[]> parseMessage(String message) {
        try {
            String[] parts = message.split(MESSAGE_REGEX);
            if (parts.length == 2 && parts[0].startsWith(TO_PREFIX)) {
                String toUserId = parts[0].substring(TO_PREFIX.length());
                return Optional.of(new String[]{toUserId, parts[1]});
            }
        } catch (Exception e) {
            log.error("解析消息失败: {}, 原始消息: {}", e.getMessage(), message);
        }
        return Optional.empty();
    }

    private static void sendToUser(Long userId, String message) {
        Optional.ofNullable(WebSocketEndpoint.getUserSession(userId))
                .ifPresent(session -> sendTextMessage(session, message));
    }

    private static void sendTextMessage(Session session, String message) {
        try {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(message);
                log.debug("消息发送成功, sessionId: {}", session.getId());
            }
        } catch (IOException e) {
            log.error("发送消息失败, sessionId: {}: {}", session.getId(), e.getMessage());
        }
    }

    private static Optional<Session> getAdminSession() {
        SessionInfo info = adminSessions.get(ADMIN_KEY);
        return info != null ? Optional.of(info.session) : Optional.empty();
    }

    /**
     * 获取调度器状态信息（用于监控）
     */
    public static String getSchedulerStatus() {
        ThreadPoolExecutor executor = (ThreadPoolExecutor) scheduler;
        return String.format(
                "客服调度器状态: 活跃线程=%d, 池大小=%d, 队列大小=%d, 已完成任务=%d",
                executor.getActiveCount(),
                executor.getPoolSize(),
                executor.getQueue().size(),
                executor.getCompletedTaskCount()
        );
    }
}